package com.yc.io;

import lombok.extern.slf4j.Slf4j;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.Socket;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * IO 一直是软件开发中的核心部分之一
 * Java 提供了哪些 IO 方式？ NIO 如何实现多路复用？
 * NIO 能解决什么问题？
 * 下面我通过一个典型场景，来分析为什么需要 NIO，为什么需要多路复用。
 * 设想，我们需要实现一个服务器应用，只简单要求能够同时服务多个客户端请求即可。
 * 使用 java.io 和 java.net 中的同步、阻塞式 API，可以简单实现。
 *
 * 实现要点：
 * 服务器端启动 ServerSocket，端口 0 表示自动绑定一个空闲端口
 * 调用accept方法，阻塞等待客户端连接
 * 利用socket模拟了一个简单的客户端，只进行连接、读取、打印
 * 当连接建立后，启动一个单独线程负责回复客户端请求。
 */
public class IOTest extends Thread{

	private ServerSocket serverSocket;

	public int getPort() {
		return serverSocket.getLocalPort();
	}

	public void run() {
		try {
//			serverSocket = new ServerSocket(0);
//			while (true) {
//				Socket socket = serverSocket.accept();
//				RequestHandlerThread requestHandlerThread = new RequestHandlerThread(socket);
//				requestHandlerThread.start();
//			}

			// 引入线程池机制来避免资源浪费
			serverSocket = new ServerSocket(0);
			ExecutorService executorService = Executors.newFixedThreadPool(8);
			while (true) {
				Socket socket = serverSocket.accept();
				RequestHandlerThread requestHandlerThread = new RequestHandlerThread(socket);
				executorService.execute(requestHandlerThread);
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if (serverSocket != null){
				try {
					serverSocket.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
			}
		}
	}

	public static void main(String[] args) {
		IOTest server = new IOTest();
		server.start();
		try {
			Socket client = new Socket(InetAddress.getLocalHost(), server.getPort());
			BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(client.getInputStream()));
			bufferedReader.lines().forEach(e -> System.out.println(e));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	class RequestHandlerThread extends Thread {
		private Socket socket;

		RequestHandlerThread (Socket socket) {
			this.socket = socket;
		}

		@Override
		public void run() {
			PrintWriter out = null;
			try {
				out = new PrintWriter(socket.getOutputStream());
			} catch (IOException e) {
				e.printStackTrace();
			}
			out.println("hello world!");
			out.flush();
		}

	}
}

